#ifndef GAME_H
#define GAME_H

#include "interface.h"
#include "render_util.h"

#define MAP_WIDTH 28
#define MAP_HEIGHT 36

#define DIR_RIGHT 0
#define DIR_DOWN 1
#define DIR_LEFT 2
#define DIR_UP 3

#define REFERENCE_SPEED 1.3f //1.262626270833333f

#define CHAR_PELLET 16
#define CHAR_POWER_PELLET 20
#define CHAR_EMPTY 255

#define GAME_TILE_EMPTY '\0'
#define GAME_TILE_SOLID '\1'
#define GAME_TILE_PELLET '\2'
#define GAME_TILE_POWER_PELLET '\3'

#define obj_cell_x(object) ((object)->x < 0 ? (object)->x / 8 - 1 : (object)->x / 8)
#define obj_cell_y(object) ((object)->y < 0 ? (object)->y / 8 - 1 : (object)->y / 8)

typedef struct LevelSpecs {
    uint8_t bonus;
    float player_speed;
    float player_power_speed;
    float ghost_speed;
    float frightened_speed;
    float ghost_tunnel_speed;

    int elroy1_dots;
    float elroy1_speed;
    int elroy2_dots;
    float elroy2_speed;

    int fright_secs;
    int flash_count;
} LevelSpecs;

extern LevelSpecs LEVEL_SPECS[21];

typedef struct Player {
    int x;
    int y;
    int sprite;

    int dx;
    int dy;
    int direction;

    int next_dx;
    int next_dy;
    int next_direction;

    // this is for subpixel movement
    float subpixel;

    // pac-man will stop for 1 frame when he eats a dot
    // and 3 frames when eating an energizer
    int stop_timer;

    int frame;
    int is_on_wall;

    float speed;

    int die_anim;
    int die_anim_time;
    int pellet_eat;
} Player;

typedef struct Ghost Ghost;

typedef struct GhostAI {
    int palette;

    // dynamic target
    int target_x;
    int target_y;

    // scatter target
    int scatter_x;
    int scatter_y;
    int home_x;

    void (*recalculate_goal)(Ghost* ghost, Game* game);
    float (*base_speed)(Ghost* ghost, Game* game);
    int (*is_dot_tracker_reached)(int level_index, int dot_count);
    int (*is_global_dot_tracker_reached)(int level_index, int dot_count);
} GhostAI;

typedef struct Ghost {
    int x;
    int y;
    int sprite;

    int dx;
    int dy;
    int direction;

    int next_cx;
    int next_cy;

    int next_dir;
    float path_dists[4];

    // this is for subpixel movement
    float subpixel;
    float speed;

    int frame;

    GhostAI ai;
    int fright;
    int eaten;
    int reverse;

    int in_house;
    int is_leaving_house;
    int is_entering_house;

    int dot_counter;

    int scatter;
} Ghost;

typedef struct Game {
    GameInterface* interface;

    uint8_t map[TILE_WIDTH][TILE_HEIGHT];
    Player player;
    Ghost ghosts[4];

    uint8_t level_index;
    int points;

    int fright_ticker;
    int flashes_remaining;
    int flash_ticker;
    int dots_left;

    int fruit_ticker;
    int fruit_point_display_timer;

    int is_retry;
    int ready_ticker;
    int player_die_ticker;
    int player_lives;
    int player_die;

    int ghosts_eaten;
    Ghost* eaten_ghost;
    int eating_tick;
    int play_return_sound;

    int ghost_dot_count;
    int use_ghost_dot_count;
    int ticks_since_dot_eaten;

    int wave;
    int wave_ticks_remaining;

    int frame;
    int debug;
    int jingle_tick;
    int restart;

    uint16_t rng_index;
} Game;

extern void game_init(Game* game, GameInterface* interface);
extern void game_reset(Game* game, int is_retry);
extern void game_free(Game* game);
extern void game_tick(Game* game);
extern void game_draw(Game* game, DisplayUtilState* render);
extern uint8_t game_get_tile(Game* game, int x, int y);
extern void game_set_tile(Game *game, int x, int y, uint8_t value);
extern int game_is_tile_solid(Game* game, int x, int y);

extern void game_player_ate_dot(Game* game);
extern void game_player_ate_ghost(Game* game, Ghost* ghost);
extern void game_player_ate_power_pellet(Game* game);
extern void game_player_ghost_touch_player(Game* game);

extern int game_ghost_flash(Game* game);
extern int game_player_has_power_pill(Game* game);

extern void player_init(Player* player);
extern void player_update(Player* self, Game* game);
extern void player_update_inputs(Player* self, Game* game);
extern void player_draw(Player* self, DisplayUtilState* render);
extern void player_begin_die_animation(Player* self);

extern void ghost_init(Ghost* ghost, Game* game, int direction, GhostAI ai);
extern void ghost_update(Ghost* ghost, Game* game);
extern void ghost_draw(Ghost* ghost, Game* game, DisplayUtilState* render);
extern void ghost_frighten(Ghost* ghost);
extern void ghost_defrighten(Ghost* ghost);
extern void ghost_leave_house(Ghost* ghost);
extern void ghost_left_house(Ghost* ghost, Game* game);
extern void ghost_set_scatter(Ghost* ghost, int value);

extern void ghost_ai_blinky(GhostAI* ai);
extern void ghost_ai_pinky(GhostAI* ai);
extern void ghost_ai_inky(GhostAI* ai);
extern void ghost_ai_clyde(GhostAI* ai);

extern uint8_t game_get_random_number(Game* game);

#endif